package main

import "fmt"

func main() {
	fmt.Println(sum(3, 6))
	myPrint()
	fmt.Println(getMyStr())
	fmt.Println(mul(9, 9))

	fmt.Println(getMyInfo())

	myFun1(1, 2, 3, "aaa", true, false)

	myFun2("bbb", 3, 1, 2, 3, 4, 5, 1)

	//匿名函数：  只定义函数体 直接调用
	sum := func(x, y int) int {
		return x + y
	}(1, 2)
	fmt.Println(sum)

	deferDemo()

	fmt.Println("===========defer===========")
	fmt.Println(defer1())
	fmt.Println(defer2())
	fmt.Println(defer3())
	fmt.Println(defer4())

	myFun := func(x int, y int) int {
		return x * y
	}
	funTypeDemo(myFun)()

	//闭包
	bibao1(bibao3(bibao2, 10, 13))
}

//1. 定义一个函数
//golang是强类型语言  形参和返回值必须定义类型
//func(参数1 类型1, 参数2 类型2, ...)(返回值名称 返回值类型){...}
func sum(x int, y int) (ret int) {
	return x + y
}

//无参 无返回值的函数
func myPrint() {
	fmt.Println("ConghuCai...")
}

//无参  有返回值
func getMyStr() string { //返回值也可以不命名  直接写一个类型  命名是为了对返回值直接操作使用
	return "conghucai"
}

//使用了命名返回值的函数  返回值可以在return后省略	//注意这种指定返回值名称的方式！   返回的时候  就算返回的不是这个变量是其他的值
//																				也会把  准备返回的其他变量的值  赋给这个返回变量
func mul(x int, y int) (ret int) {
	ret = x * y
	return //省略不写ret
}

//返回多个返回值
func getMyInfo() (string, string, string) {
	//按照定义的这个顺序接收返回值  如果有用不到的值  用_匿名变量接收
	return "conghucai", "male", "HFUT"
}

//参数类型简写  当连续几个参数类型一致时  可以统一声明类型
func myFun1(x, y, z int, str string, a, b bool) {
	fmt.Println(x, str, a)
}

//可变长参数  可传入任意个同类型的参数(0-...)
func myFun2(str string, x ...int) {
	fmt.Println(str)
	fmt.Printf("%T\n", x) //[]int  x实际上是切片接收  slice
}

//2. defer
func deferDemo() {
	fmt.Println("111111")
	defer fmt.Println("2-11111") //defer后的语句  延迟到 函数将结束的时候再执行
	defer fmt.Println("2-22222")
	defer fmt.Println("2-33333")
	defer fmt.Println("2-44444")
	defer fmt.Println("2-55555")
	fmt.Println("33333333")

	//使用场景：关闭文件  关闭流  资源回收

	//defer原理 ： 把每个语句存储到一个 栈 里面  结束时出栈依次执行
	//defer输出顺序：
	/* 	2-55555
	   	2-44444
	   	2-33333
	   	2-22222
	   	2-11111 */ //出栈的顺序

	//底层return语句实现：golang return操作不是一个原子操作  而是分两步：
	//第一步 ： 返回值赋值
	//第二步 ： ret 返回
	//如果有defer  那么在一步 二步之间 执行defer
}

//defer经典应用场景
//1
func defer1() int {
	x := 5
	defer func() {
		x++ //已经确认返回值是5  修改的就不是返回值本身了
	}()
	return x
} // 5

//2
func defer2() (x int) {
	defer func() {
		x++
	}()
	return 5
} //6   提前定义好了返回的是x   因此defer修改的就是返回值本身

//3
func defer3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x
} //5   返回的一定是y  return x的操作只不过把x的值赋给了y   因此defer修改的也只是x变量

//4
func defer4() (x int) {
	defer func(x int) { //函数传入int  拷贝策略  只是把值拷贝进来了
		x++ //这个x不是返回值的那个x
	}(x)
	return 5
} //5	这个地方  defer内部函数的x  和返回值定义的x变量不是一回事   defer内部函数的x传入的只是值

//3. 函数作用域
//全局 - 局部 ： 如果局部变量和全局变量重名，优先访问局部变量。

//4. 函数类型的变量
//func(int)int	--参数为int  返回值为int的函数
//func()   空参void函数
//函数可以作为一个变量  既可作参数  也可做返回值
func funTypeDemo(x func(int, int) int) func() {
	res := x(9, 9)
	ret := func() {
		fmt.Println(res)
	}
	return ret
}

//4. 闭包
//应用场景：
func bibao1(f func()) {
	fmt.Println("bibao1...")
	f()
}

func bibao2(x, y int) {
	fmt.Println("bibao2...")
	fmt.Println(x + y)
}

//现在想用bibao1去接收bibao2作为参数  但是显然闭包bibao2是有参的  而bibao1只能接收无参的函数
//解决方法：闭包   定义一个新的函数  这个函数可以接收原来的bibao2函数  并返回一个新函数  使其满足bibao1的类型要求
//闭包的本质就是一个函数  其包含了函数外部作用域的一个对象
func bibao3(f func(int, int), x, y int) func() {
	temp := func() {
		fmt.Println("bibao3...")
		f(x, y) //本质上就是对原函数进行了进一步封装  使其对外的参数和返回类型发生改变！
	}
	return temp
}

//闭包依赖的原理：1.函数作为参数和返回值   2.变量的作用域
//func bibaoFun(fun func(int,string,...)  s string, x int) int {}   函数+外部变量
